#include "treehandler.h"

using namespace invertedindex;


TreeHandler::TreeHandler(const std::string& confPath):
	kvf(),
	kkvf(),
	bsharpTree(getNodeSize(confPath),kvf,kkvf,"./data/bstree.dat"){

}

TreeHandler::~TreeHandler(){
}

bool TreeHandler::getWord(WordContainer& thdata){
	bool ret = false;
	//Busco en bs
	std::pair<unsigned int, char*> wrd = thdata.getWord();
	KeyVal kv(wrd.first, wrd.second, thdata.getWordId());
	if(!bsharpTree.search(kv)){
		delete[] wrd.second;
		return ret;
	}

	//paso front coding en registro obtenido a frontcoding converter
	std::pair<unsigned int, char*> keyWord = kv.getKey();
	WordContainer wcKey(keyWord.first, keyWord.second, kv.getIdKey());
	BlockPointer* bp=kv.getBP();
	wcKey.setBP(bp);
	delete bp;
	std::pair<unsigned int, char*> lexico = kv.getLexico();
	FrontCodingManager fcm(wcKey, kv.getWordCount(),lexico.second);


	//busco si esta palabra buscada
	WordContainer wcTHData(wrd.first, wrd.second, thdata.getWordId());
	if(fcm.searchWord(wcTHData)){
		//armo thdata con info
		thdata.setFields(wcTHData);
		ret = true;
	}
	
	delete[] wrd.second;
	delete[] keyWord.second;
	delete[] lexico.second;

	return ret;
}

bool TreeHandler::insertWord(const WordContainer& thdata){

	bool ret = true;
	BlockPointer* bpAux;
	//Busco en bs registro donde deberia estar
	std::pair<unsigned int, char*> wrd = thdata.getWord();
	KeyVal kv(wrd.first, wrd.second, thdata.getWordId());
	if(!bsharpTree.search(kv)){
		//insertar nuevo registro con palabra sola porque arbol vacio
		bpAux=thdata.getBP();
		kv.setBP(bpAux);
		delete bpAux;
		kv.setWordCount(0);
		ret = bsharpTree.insert(kv);
		delete[] wrd.second;
		return ret;
	}

	//Descomprimo frontcoding
	std::pair<unsigned int, char*> keyWord = kv.getKey();
	WordContainer wcKey(keyWord.first, keyWord.second, kv.getIdKey());
	bpAux=kv.getBP();
	wcKey.setBP(bpAux);
	delete bpAux;

	std::pair<unsigned int, char*> lexico = kv.getLexico();
	FrontCodingManager fcm(wcKey, kv.getWordCount(),lexico.second);

	//Si esta retorno error
	WordContainer wcTHData(wrd.first, wrd.second, thdata.getWordId());
	wcTHData.copyBP(thdata);

	if(fcm.searchWord(wcTHData)){
		delete[] wrd.second;
		delete[] keyWord.second;
		delete[] lexico.second;
		return false;
	}

	//Inserto la palabra
	fcm.insertWord(wcTHData);

	//Guardo en disco
	//Veo si hay overflow llamo handle Overflow de ser necesario
	std::pair<unsigned int, char*> frontCoding = fcm.getFrontCoding();
	WordContainer* wcBaseO=fcm.getBase();
	std::pair<unsigned int,char*> baseWordO = wcBaseO->getWord();
	unsigned int registerFinalLength=frontCoding.first+baseWordO.first+sizeof(unsigned int)*5;

	FrontCodingManager* newRegister;
	if(registerFinalLength > REGISTER_MAX_SIZE){

		//parto nodo y los guardo
		newRegister = fcm.divide();

		//Guardo Registro anterior
		std::pair<unsigned int, char*> frontCodingA = fcm.getFrontCoding();
		WordContainer* wcNBA=fcm.getBase();
		std::pair<unsigned int,char*> baseWordA = wcNBA->getWord();
		KeyVal kvSA(baseWordA.first, baseWordA.second, wcNBA->getWordId());
		bpAux=wcNBA->getBP();
		kvSA.setBP(bpAux);
		delete bpAux;

		kvSA.setWordCount(fcm.getWordCount());
		kvSA.setLexico(frontCodingA.first, frontCodingA.second);
		//Guardo registro en arbol
		//ret=bsharpTree.modify(kvSA);
		ret=bsharpTree.replace(kvSA,kv);
		delete wcNBA;
		delete[] baseWordA.second;
		delete[] frontCodingA.second;

		if(ret==true){
			//Genero Registro Nuevo
			std::pair<unsigned int, char*> frontCodingN = newRegister->getFrontCoding();
			WordContainer* wcNBN=newRegister->getBase();
			std::pair<unsigned int,char*> baseWordN = wcNBN->getWord();
			KeyVal kvSN(baseWordN.first, baseWordN.second, wcNBN->getWordId());
			bpAux=wcNBN->getBP();
			kvSN.setBP(bpAux);
			delete bpAux;

			kvSN.setWordCount(newRegister->getWordCount());
			kvSN.setLexico(frontCodingN.first, frontCodingN.second);
			//Guardo registro en arbol
			ret=bsharpTree.insert(kvSN);
			delete wcNBN;
			delete[] baseWordN.second;
			delete[] frontCodingN.second;
		}


		delete newRegister;
	}
	else{
		//Armo nuevo registro
		WordContainer* wcNB=fcm.getBase();
		std::pair<unsigned int,char*> baseWord = wcNB->getWord();
		KeyVal kvS(baseWord.first, baseWord.second, wcNB->getWordId());
		bpAux=wcNB->getBP();
		kvS.setBP(bpAux);
		delete bpAux;

		kvS.setWordCount(fcm.getWordCount());
		kvS.setLexico(frontCoding.first, frontCoding.second);
		//Guardo registro en arbol
		ret=bsharpTree.replace(kvS,kv);

		delete wcNB;
		delete[] baseWord.second;
	}

	delete wcBaseO;	
	delete[] baseWordO.second;
	delete[] frontCoding.second;
	delete[] wrd.second;
	delete[] keyWord.second;
	delete[] lexico.second;
	return ret;
}

bool TreeHandler::modifyWordBlockPointer(const WordContainer& thdata){

	bool ret = false;
	BlockPointer* bpAux;

	//Busco en bs registro donde deberia estar
	std::pair<unsigned int, char*> wrd = thdata.getWord();
	KeyVal kv(wrd.first, wrd.second, thdata.getWordId());
	if(!bsharpTree.search(kv)){
		delete[] wrd.second;
		return ret;
	}

	//Descomprimo frontcoding
	std::pair<unsigned int, char*> keyWord = kv.getKey();
	WordContainer wcKey(keyWord.first, keyWord.second, kv.getIdKey());
	bpAux=kv.getBP();
	wcKey.setBP(bpAux);
	delete bpAux;

	std::pair<unsigned int, char*> lexico = kv.getLexico();
	FrontCodingManager fcm(wcKey, kv.getWordCount(),lexico.second);

	//Si no esta retorno error
	WordContainer wcTHData(wrd.first, wrd.second, thdata.getWordId());
	wcTHData.copyBP(thdata);

	if(!fcm.searchWord(wcTHData)){
		delete[] wrd.second;
		delete[] keyWord.second;
		delete[] lexico.second;
		return false;
	}
	wcTHData.copyBP(thdata);


	//Inserto la palabra
	if(fcm.modifyWord(wcTHData)){
		//Armo nuevo registro
		std::pair<unsigned int, char*> frontCoding = fcm.getFrontCoding();
		WordContainer* wcNB=fcm.getBase();
		std::pair<unsigned int,char*> baseWord = wcNB->getWord();
		KeyVal kvS(baseWord.first, baseWord.second, wcNB->getWordId());
		bpAux=wcNB->getBP();
		kvS.setBP(bpAux);
		delete bpAux;

		kvS.setWordCount(fcm.getWordCount());
		kvS.setLexico(frontCoding.first, frontCoding.second);
		//Guardo registro en arbol
		ret=bsharpTree.replace(kvS,kv);

		delete[] frontCoding.second;
		delete wcNB;
		delete[] baseWord.second;
	}

	delete[] wrd.second;
	delete[] keyWord.second;
	delete[] lexico.second;
	return ret;
}


void TreeHandler::toOstream(std::ostream& out){
	out << bsharpTree;
}

std::ostream& TreeHandler::printTreeBlock(std::ostream& out, unsigned int offset){
	if(offset%BLOCK_SIZE!=0){
		out<<"El offset de bloque no es multiplo de tamaño de bloque"<<std::endl;
		return out;
	}
	return bsharpTree.toOstreamFC(out, offset);
}

unsigned int TreeHandler::getNodeSize(const std::string& confPath){
	std::ifstream in;
	std::string file=confPath+"bstree.conf";
	in.open(file.c_str());
	if ( ! in ) {
		return 1024;
	}
	std::string str;
	std::getline(in,str);
	int i=0;
	int j;
	unsigned int datI;
	bool datB;
	std::string aux;
	while ( in ) {
		if(str[0]=='_'){
			aux="";
			j=0;
			while(str[j]!='=') j++;
			j++;
			while(str[j]!=';'){
				aux+=str[j];
				j++;
			}

			char * pEnd;
			if(i==0)
				BLOCK_SIZE = strtol (aux.c_str(),&pEnd,10);
			if(i==1)
				REGISTER_MAX_SIZE = strtol (aux.c_str(),&pEnd,10);
			i++;
		}
		getline(in,str);
	}
	if(i<1){
		std::cout<<"error con archivo .conf de bsharp\n";
		exit(-1);
	}
		

	in.close();
	
	return BLOCK_SIZE;
}


